package com.study.demo.aop.demo2.parser;


import com.study.demo.aop.demo2.BeanFactoryElvinOperationSourceAdvisor;
import com.study.demo.aop.demo2.interceptor.ElvinInterceptor;
import org.springframework.aop.config.AopNamespaceUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

/**
 * @author: elvin
 * @time: 2019-01-24 09:09
 * @desc:
 **/
public class AnnotationDrivenElvinBeanDefinitionParser implements BeanDefinitionParser {

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        registerElvinAdvisor(element, parserContext);
        return null;
    }

    private void registerElvinAdvisor(Element element, ParserContext parserContext) {
        AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
        AnnotationDrivenElvinBeanDefinitionParser.SpringElvinConfigurer.registerElvinAdvisor(element, parserContext);
    }

    private static class SpringElvinConfigurer {

        private static void registerElvinAdvisor(Element element, ParserContext parserContext) {

            Object eleSource = parserContext.extractSource(element);

            RootBeanDefinition sourceDef = new RootBeanDefinition("com.study.demo.aop.demo2.operationsource.AnnotationElvinOperationSource");
            sourceDef.setSource(eleSource);
            sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

            RootBeanDefinition interceptorDef = new RootBeanDefinition(ElvinInterceptor.class);
            interceptorDef.setSource(eleSource);
            interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            parseErrorHandler(element, interceptorDef);
            //ElvinNamespaceHandler.parseKeyGenerator(element, interceptorDef);
            interceptorDef.getPropertyValues().add("elvinOperationSources", new RuntimeBeanReference(sourceName));
            String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);

            RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryElvinOperationSourceAdvisor.class);
            advisorDef.setSource(eleSource);
            advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            advisorDef.getPropertyValues().add("elvinOperationSource", new RuntimeBeanReference(sourceName));
            advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
            if (element.hasAttribute("order")) {
                advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
            }
            parserContext.getRegistry().registerBeanDefinition("com.study.demo.aop.demo2.internalElvinAdvisor", advisorDef);

            CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
            compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
            compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
            compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, "com.study.demo.aop.demo2.internalElvinAdvisor"));
            parserContext.registerComponent(compositeDef);
        }
    }

    private static BeanDefinition parseErrorHandler(Element element, BeanDefinition def) {
        String name = element.getAttribute("error-handler");
        if (StringUtils.hasText(name)) {
            def.getPropertyValues().add("errorHandler", new RuntimeBeanReference(name.trim()));
        }
        return def;
    }
}
